package com.joesem.sale.client.base

import android.app.Application
import android.content.Intent
import android.util.Log
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.joesem.sale.client.MyApplication
import com.joesem.sale.client.db.LoginTable
import com.joesem.sale.client.network.ApiService
import com.joesem.sale.client.network.LoginException
import com.joesem.sale.client.network.data.CallResponse
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking


abstract class BaseViewModel(application: Application) : AndroidViewModel(application) {
    val loadingStateData = MutableLiveData<LoadingState>()
    val loginStateData = MutableLiveData<LoginError>()
    val uiStateData = MutableLiveData<Any?>()

    sealed class LoadingState {
        object LoadingShow : LoadingState()
        object LoadingDismiss : LoadingState()
    }

    object LoginError

    fun actionIntent(intent: Any) {
        uiStateData.value = intent
    }

    fun actionPostIntent(intent: Any) {
        uiStateData.postValue(intent)
    }

}

fun <T> CallResponse<T?>.isSuccess(
    successAction: (CallResponse<T?>) -> Unit = {},
    failed: (Throwable) -> Unit = {}
): CallResponse<T?> {
    return this.apply {
        try {
            if (code == 0) {
                successAction.invoke(this)
            } else {
                val t = if (code == 401) {
                    LoginException(msg)
                } else
                    Exception(msg)
                failed.invoke(t)
                throw t
            }
        } catch (e: Throwable) {
            failed.invoke(e)
            throw e
        }

    }
}

fun <T> CallResponse<T?>.isSuccess() = code == 0

fun <T> CoroutineScope.request(
    onLoadingIn: () -> Unit,
    onLoadingQuit: () -> Unit,
    request: suspend ApiService.() -> CallResponse<T>,
    response: (CallResponse<T>) -> Unit,
    responseFailed: (Throwable) -> Unit
) {
    launch {
        flow {
            emit(request.invoke(ApiService.service))
        }.onStart {
            onLoadingIn.invoke()
            Log.i("request", "request: start")
        }.onCompletion {
            onLoadingQuit.invoke()
            Log.i("request", "request Completion: $it")
        }.onEach {
            Log.i("request", "request onEach: $it")
            if (it.code != 0) {
                if (it.code == 401) {
                    throw LoginException(it.msg.orEmpty())
                } else
                    throw Exception(it.msg)
            }
        }.catch {
            Log.i("request", "request err: $it")
            responseFailed.invoke(it)
        }.collect {
            response.invoke(it)
            Log.i("request", "request collect: $it")
        }
    }
}

fun <T> BaseViewModel.request(
    isLoading: Boolean = false,
    request: suspend ApiService.() -> CallResponse<T>,
    response: (CallResponse<T>) -> Unit,
    responseFailed: (Throwable) -> Unit
) {
    viewModelScope.request(
        onLoadingIn = {
            if (isLoading) {
                loadingStateData.value = BaseViewModel.LoadingState.LoadingShow
            }
        },
        onLoadingQuit = {
            if (isLoading) {
                loadingStateData.value = BaseViewModel.LoadingState.LoadingDismiss
            }
        },
        request = request,
        response = response,
        responseFailed = {
            if (it is LoginException) {
                loginStateData.value = BaseViewModel.LoginError
                LoginTable.table.clearAccount()
            }
            responseFailed.invoke(it)
        }

    )
}

